package ign.agents;

import ign.MyListener;
import ign.ProgramParams;
import ign.behaviours.ihm.CheckConflicts;
import ign.behaviours.ihm.Listening;
import ign.gui.Gui;
import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.domain.FIPAException;
import jade.wrapper.ControllerException;
import java.awt.Rectangle;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.FileWriter;
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;

/**
 * Cette classe représente l'agent IHM, qui va se charge de repérer
 * les conflits et d'afficher un résultat visuel du comportement
 * des autres agents.
 * @author Simon Bourigaut & Ghislain Di Paolo
 */
public class IHM extends Agent implements WindowListener {
	private static final long serialVersionUID = 2889004986684352911L;
	
	private HashMap<AID, Rectangle> positions;
	private HashMap<AID, Rectangle> original_positions;
	private Gui gui;
	private CheckConflicts myCheckConflict ;
	private MyListener listener;
	
	public IHM() throws ControllerException {
		
		positions = new HashMap<AID, Rectangle>();
		original_positions = new HashMap<AID, Rectangle>();
 	}
	
	/**
	 * Change la position d'un AID.
	 * @param aid	l'agent en question
	 * @param r sa nouvelle position affichée.
	 */
	public void putPosition(AID aid, Rectangle r){
		
		if(!positions.containsKey(aid))
			return ;
		
		positions.put(aid, r);
		resumeChecking();
		
		repaint();
	}
	
	/**
	 * Assigne un nouvel agent aux connaissances de l'ihm.
	 * @param aid Le numéro de l'agent
	 * @param r sa position
	 */
	public void addBuilding(AID aid, Rectangle r){
		
		positions.put(aid, r);
		original_positions.put(aid, r);
		resumeChecking();
		
		repaint();
	}
	
	/**
	 * Renvoi toutes les positions connues de l'ihm.
	 * @return Un ensemble de paires agent, position.
	 */
	public Map<AID, Rectangle> getAllPositions(){
		
		return positions;
	}
	
	/**
	 * Redessine l'affichage.
	 */
	public void repaint() {
		
		gui.clear();
		
		for (Rectangle r : positions.values())
			gui.addRectangle(r);

		Thread.yield();
		gui.repaint();
	}
	
	@Override
	protected void setup(){
		
		DFAgentDescription d = new DFAgentDescription();
		d.setName(getAID());
		
		ServiceDescription sd = new ServiceDescription();
		sd.setType("IHM");
		sd.setName("IHM");
		d.addServices(sd);

		try {
			DFService.register(this, d);
		} catch (FIPAException e) {
			Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
		}
		
		addBehaviour(new Listening());
		
		this.myCheckConflict = new CheckConflicts();
		
		addBehaviour(this.myCheckConflict);
		
		listener = new MyListener() ;
		try {
			
			this.getContainerController().addPlatformListener(listener);
			
		} catch (ControllerException e) {
			Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
		}
		
		/* Creation de la fenêtre */
		gui = new Gui(listener);
		gui.setDoubleBuffered(true);
		gui.setVisible(true);
		
		JFrame jf = new JFrame();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setTitle("Institut Geographique National");
		jf.setSize(ProgramParams.window_width, ProgramParams.window_height);
		jf.setVisible(true);
		jf.setLocationRelativeTo(null);
		jf.setContentPane(gui);
		jf.addWindowListener(this);
	}
	
	@Override
	protected void takeDown(){
	}

	/**
	 * Supprime un agent des connaissances de l'ihm.
	 * @param aid 
	 */
	public void remove(AID aid) {
		
		positions.remove(aid);
		repaint();
	}
	
	/**
	 * Met en pause le Behaviour qui teste l'existence de conflits.
	 */
	public void stopChecking() {
		
		this.myCheckConflict.block();
	}
	
	/**
	 * Met en fonctionnement le Behaviour qui teste l'existence de conflits.
	 */
	public void resumeChecking() {
		
		this.myCheckConflict.restart();
	}

	@Override
	public void windowOpened(WindowEvent e) {
	}

	@Override
	public void windowClosing(WindowEvent e) {
		
		if ( !ProgramParams.write_statistics)
			return;
		
		/* Affiche le nombre de morts */
		ArrayList<SimpleEntry<Long, Integer>> data = listener.getData();
		String plot_name = "plot_" + ProgramParams.move_priority;
		String ret = plot_name  + " = [0,0";
		
		for (SimpleEntry<Long, Integer> en : data) 
			ret += ";" + en.getValue() + "," + en.getKey();
		
		ret += "];\nplot(" + plot_name + "(:,2), " + plot_name + "(:, 1));\n";
		
		/* Distance de la position originale */
		int i = 0, dist[] = new int[positions.size()];
		double mean = 0, std_dev = 0;
		for (AID aid : positions.keySet()) {
			Rectangle now = positions.get(aid);
			Rectangle before = original_positions.get(aid);
			
			int pnow = (now.x + now.width) / 2 + (now.y + now.height) / 2;
			int pbefore = (before.x + before.width) / 2 + (before.y + before.height) / 2;
			int cur_dist = Math.abs(pnow - pbefore);
			dist[i++] = cur_dist;
			mean += cur_dist;
		}
		mean /= dist.length;
		
		for (int j = 0; j < dist.length; j++) {
			double temp = dist[j] - mean;
			std_dev += temp * temp;
		}
		std_dev /= dist.length;
		std_dev = Math.sqrt(std_dev);
		
		ret = "% " + plot_name + ", mean : " + mean + " +/- " + std_dev + "\n" + ret;
		
		try {
			FileWriter f = new FileWriter(ProgramParams.stat_folder + "data_" + plot_name + ".m");
			f.write(ret);
			f.close();
		} catch (IOException ex) {
			Logger.getLogger(IHM.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	@Override
	public void windowClosed(WindowEvent e) {
	}

	@Override
	public void windowIconified(WindowEvent e) {
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
	}

	@Override
	public void windowActivated(WindowEvent e) {
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
	}
}
